﻿#region using

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

#endregion

namespace JupiterLibrary
{
    public struct MeshNode
    {
        public Mesh Mesh;
        public Node Node;
    }

    public static class MeshUtil
    {
        public static List<MeshNode> ConvertFBXToMesh(Model model, Scene scene, Matrix matrix)
        {
            return ConvertModelToMesh(model, scene, ref matrix);
        }

        public static List<MeshNode> ConvertFBXToMesh(Model model, Scene scene)
        {
            Matrix identity = Matrix.Identity;

            return ConvertModelToMesh(model, scene, ref identity);
        }

        public static List<MeshNode> ConvertModelToMesh(Model model, Scene scene, ref Matrix matrix)
        {
            List<MeshNode> list = new List<MeshNode>();

            bool aplicarMatriz = (matrix != Matrix.Identity);

            foreach (ModelMesh modelMesh in model.Meshes)
            {
                // Creamos el mesh
                Mesh targetMesh=new Mesh();

                MeshNode meshNode = new MeshNode();

                meshNode.Mesh = targetMesh;

                if (scene != null)
                {
                    Texture2D texture = ((BasicEffect)modelMesh.Effects[0]).Texture;

                    targetMesh.Texture = texture;

                    // Creamos el nodo en la escena relacionado con este mesh
                    Node node = scene.CreateNode(targetMesh);

                    meshNode.Node = node;
                }

                list.Add(meshNode);

                // Todas las ModelMeshPart comparten el mismo Vertex e IndexBuffer

                ModelMeshPart meshPart = modelMesh.MeshParts[0];

                // Creamos el VertexBuffer

                VertexElement[] elements = meshPart.VertexBuffer.VertexDeclaration.GetVertexElements();

                int floatStride = meshPart.VertexBuffer.VertexDeclaration.VertexStride / sizeof(float);

                float[] data = new float[meshPart.VertexBuffer.VertexCount * floatStride];

                meshPart.VertexBuffer.GetData<float>(data);

                foreach (VertexElement element in elements)
                {
                    if (element.VertexElementUsage == VertexElementUsage.Position)
                    {
                        if (targetMesh.VertexBuffer.Position == null)
                        {
                            targetMesh.VertexBuffer.Position = new Vector3[meshPart.VertexBuffer.VertexCount];
                        }

                        int index = element.Offset / sizeof(float);

                        for (int j = 0; j < meshPart.VertexBuffer.VertexCount; j++)
                        {
                            targetMesh.VertexBuffer.Position[j].X = data[index];
                            targetMesh.VertexBuffer.Position[j].Y = data[index + 1];
                            targetMesh.VertexBuffer.Position[j].Z = data[index + 2];

                            index += floatStride;
                        }
                    }
                    
                    if (element.VertexElementUsage == VertexElementUsage.Normal)
                    {
                        if (targetMesh.VertexBuffer.Normal == null)
                        {
                            targetMesh.VertexBuffer.Normal = new Vector3[meshPart.VertexBuffer.VertexCount];
                        }

                        int index = element.Offset / sizeof(float);

                        for (int j = 0; j < meshPart.VertexBuffer.VertexCount; j++)
                        {
                            targetMesh.VertexBuffer.Normal[j].X = data[index];
                            targetMesh.VertexBuffer.Normal[j].Y = data[index + 1];
                            targetMesh.VertexBuffer.Normal[j].Z = data[index + 2];

                            index += floatStride;
                        }
                    }
                    
                    if (element.VertexElementUsage == VertexElementUsage.TextureCoordinate)
                    {
                        if (targetMesh.VertexBuffer.TextureCoord == null)
                        {
                            targetMesh.VertexBuffer.TextureCoord = new Vector2[meshPart.VertexBuffer.VertexCount];
                        }

                        int index = element.Offset / sizeof(float);

                        for (int j = 0; j < meshPart.VertexBuffer.VertexCount; j++)
                        {
                            targetMesh.VertexBuffer.TextureCoord[j].X = data[index];
                            targetMesh.VertexBuffer.TextureCoord[j].Y = data[index + 1];

                            index += floatStride;
                        }
                    }
                }

                // Crea el IndexBuffer

                targetMesh.IndexBuffer.Indices = new uint[meshPart.IndexBuffer.IndexCount];

                ushort[] indices=new ushort[meshPart.IndexBuffer.IndexCount];
                    
                meshPart.IndexBuffer.GetData<ushort>(indices);

                for (int j = 0; j < meshPart.IndexBuffer.IndexCount; j++)
                {
                    targetMesh.IndexBuffer.Indices[j] = indices[j];
                }

                // Aplicamos la matrix si es necesario

                if (aplicarMatriz)
                {
                    targetMesh.VertexBuffer.ApplyMatrix(matrix);
                }

                // Ahora creamos los MeshParts si el modelo tiene varios

                if (modelMesh.MeshParts.Count > 1)
                {
                    targetMesh.MeshParts = new MeshPart[modelMesh.MeshParts.Count];

                    int i = 0;

                    foreach (ModelMeshPart part in modelMesh.MeshParts)
                    {
                        targetMesh.MeshParts[i].StartIndex = part.StartIndex;
                        targetMesh.MeshParts[i].VertexOffset = part.VertexOffset;
                        targetMesh.MeshParts[i].PrimitiveCount = part.PrimitiveCount;
                        targetMesh.MeshParts[i].NumVertices = part.NumVertices;

                        i++;
                    }
                }
            }

            // DESCARGAMOS ESTE MODELO DEL CONTENT

            //Jupiter.Content.Unload();

            return list;
        }
    }
}
